package com.dataart.training.crimequalification.dao;

import com.dataart.training.crimequalification.entity.UserEntity;
import com.dataart.training.crimequalification.entity.UserRoleEntity;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.List;

@SuppressWarnings("JpaQlInspection")
@Repository
public class UserDAOImpl implements UserDAO {

    // TODO: make use of the GenericDAO pattern

    // -------------------------------------------------------------

    @Autowired
    private SessionFactory sessionFactory;

    // ----------------------------------------------------------------

    /**
     * Returns list of all users present in the storage.
     *
     * @return list of all existing users
     */
    public List<UserEntity> getAllUsers() {
        return sessionFactory.getCurrentSession()
                .createQuery("from UserEntity order by lastName").list();
    }

    /**
     * Allows to look up user by a given id.
     *
     * @param id identification key to look up user with
     * @return user principal registered by id
     */
    public UserEntity getUserById(Long id) {
        Query query = sessionFactory.getCurrentSession().createQuery(
                "from UserEntity where userID = :userId");
        query.setLong("userId", id);
        List<UserEntity> li = query.list();
        return li.isEmpty() ? null : li.get(0);
    }

    /**
     * Allows to find user with pair of given credentials.
     *
     * @param login    username to look up user by
     * @param password password to look up user by
     * @return user principal registered by these username and password
     */
    public UserEntity getUserAuthorization(String login, String password) {
        Query query = sessionFactory.getCurrentSession().createQuery(
                "from UserEntity where username=:login and password=:password");
        query.setString("login", login);
        query.setString("password", password);
        List<UserEntity> li = query.list();
        return (li.isEmpty() ? null : li.get(0));
    }

    /**
     * Allows to find user by a given username.
     *
     * @param username username to look up user by
     * @return user principal registered by the username
     */
    public UserEntity getUserByUsername(String username) {
        Query query = sessionFactory.getCurrentSession().createQuery(
                "from UserEntity where username=:username");
        query.setString("username", username);
        return (UserEntity) query.uniqueResult();
    }

    /**
     * Allows to find user by a given telephone number.
     *
     * @param phoneNumber telephone number to look up user by
     * @return user principal registered by the telephone number
     */
    public UserEntity getUserByPhoneNumber(String phoneNumber) {
        Query query = sessionFactory.getCurrentSession().createQuery(
                "from UserEntity where phoneNumber=:phoneNumber");
        query.setString("phoneNumber", phoneNumber);
        return (UserEntity) query.uniqueResult();
    }

    /**
     * Saves a given user principal in the storage.
     *
     * @param user user principal to be saved
     */
    @Override
    public void addNewUser(UserEntity user) {
        this.sessionFactory.getCurrentSession().save(user);
    }

    /**
     * Updates user principal currently existing in the storage.
     *
     * @param user user principal to be updated
     */
    @Override
    public void updateUserDetails(UserEntity user) {
        this.sessionFactory.getCurrentSession().update(user);
    }

    /**
     * Grants given role to a user.
     *
     * @param user user to be assigned to role
     * @param role role to be granted
     */
    @Override
    public void assignRoleToUser(UserEntity user, String role) {
        UserRoleEntity urole = new UserRoleEntity();
        urole.setUser(user);
        urole.setAuthority(role);
        this.sessionFactory.getCurrentSession().save(urole);
    }

    /**
     * Returns a user principal registered by the given e-mail address.
     *
     * @param email e-mail address to look up user by
     * @return user registered by a given e-mail
     */
    @Override
    public UserEntity getUserByEmail(String email) {
        Query query = sessionFactory.getCurrentSession().createQuery(
                "from UserEntity where email=:email");
        query.setString("email", email);
        return (UserEntity) query.uniqueResult();
    }

    /**
     * Renders user principal verified.
     *
     * @param user user account to be verified
     */
    @Override
    public void verifyUserAccount(UserEntity user) {
        user.setVerified(true);
        sessionFactory.getCurrentSession().update(user);
    }

    /**
     * Returns list of roles granted to the given user.
     *
     * @param user user to find granted roles of
     * @return list of granted roles
     */
    @Override
    public List<UserRoleEntity> getUserRoles(UserEntity user) {
        List<UserRoleEntity> resultList = user.getRoles();
        Hibernate.initialize(resultList);
        return resultList;
    }

    /**
     * Removes given user from the persistent storage.
     *
     * @param user user entity to remove
     */
    @Override
    public void removeUser(UserEntity user) {

        UserEntity persistedUser = getUserByEmail(user.getEmail());

        for (UserRoleEntity role : persistedUser.getRoles()) {
            sessionFactory.getCurrentSession().delete(role);
        }
        sessionFactory.getCurrentSession().delete(persistedUser);
    }
}